package model;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public final class  MethodDeclared implements StatementKind, Comparable<MethodDeclared>{
	  private List<Class<?>> inputTypesCached;
	  private Class<?> outputTypeCached;
	  private boolean isStaticComputed = false;
	  private boolean isStaticCached = false;
	  private boolean isVoidComputed = false;
	  private boolean isVoidCached = false;

//	  private List<String> actualValues=new LinkedList<String>();
	  
	  private final Method method;

	  private MethodDeclared(Method method) {
		    if (method == null)
		      throw new IllegalArgumentException("method should not be null.");

		    this.method = method;
		    this.method.setAccessible(true);
		  }

		  public static MethodDeclared getMethod(Method method) {
		    return new MethodDeclared(method);
		  }
			public Method getMethod(){
				return this.method;
			}
			
			public String getMethodName(){
				return this.method.getName();
			}
		  /**
		   * Returns the input types of this method.
		   */
		  public List<Class<?>> getInputTypes() {
		    if (inputTypesCached == null) {
		      Class<?>[] methodParameterTypes = method.getParameterTypes();
		      inputTypesCached = new ArrayList<Class<?>>(
		          methodParameterTypes.length + (isStatic() ? 0 : 1));
		      for (int i = 0; i < methodParameterTypes.length; i++) {
		        inputTypesCached.add(methodParameterTypes[i]);
		      }
		    }
		    return inputTypesCached;
		  }
		  public boolean isVoid() {
			    if (!isVoidComputed) {
			      isVoidComputed = true;
			      isVoidCached = void.class.equals(this.method.getReturnType());
			    }
			    return isVoidCached;
			  }

		  public boolean isStatic() {
			    if (!isStaticComputed) {
			      isStaticComputed = true;
			      isStaticCached = Modifier.isStatic(this.method.getModifiers());
			    }
			    return this.isStaticCached;
			  }
		  public Class<?> getOutputType() {
			    if (outputTypeCached == null) {
			      outputTypeCached = method.getReturnType();
			    }
			    return outputTypeCached;
			  }
		  

		  
		  public  String toParseableString(Map<String,Class<?>> classesToTest){
			  if(classesToTest.containsKey(this.method.getDeclaringClass().getName()))
				  	return toString1(this.method);
			  return null;
		  }
		  
		  public String convertString(){
			  return this.toString1(this.method);
		  }
		  @Override
		  public String toString() {
		    return convertString();
		  }

		  public  String toString1(Method m) {
			     StringBuilder sb = new StringBuilder();
//			     sb.append(m.getDeclaringClass().getName()+"." );
			     sb.append(m.getReturnType().getName()+" ");
			     sb.append(m.getName() + "(");
			     Class<?>[] params = m.getParameterTypes();
			     for (int j = 0; j < params.length; j++) {
			    	 if(params[j].isArray())
			    		 sb.append(params[j].getComponentType().getName()+"[]");
			    	 else
			    		 sb.append(params[j].getName());
			       if (j < (params.length - 1))
			         sb.append(",");
			     }
			     sb.append(")");
			    // sb.append("\n");
			     return sb.toString();
			   }

			@Override
			public int compareTo(MethodDeclared o) {
				final int compareName = this.getMethod().getName().compareTo(o.getMethod().getName());
				if(compareName != 0) return compareName;
				if(this.getMethod().getParameterTypes().length!= o.getMethod().getParameterTypes().length) return this.getMethod().getParameterTypes().length - o.getMethod().getParameterTypes().length;
					return 0;
			}

}
